classdef (Sealed) C_constellation < handle
    %C_MODULATOR Summary of this class goes here
    %   Detailed explanation goes here
    
    properties (GetAccess='private', SetAccess='private')
        selectedModType;       
        ModType;
    end
    
    methods (Access = private)
        function this = C_constellation
            initialise(this);
        end
    end
    
    methods (Static)         
        function this = Instance()
             persistent this_obj;
             
             if isempty(this_obj) || ~isvalid(this_obj)
                this_obj = C_constellation;
             end
             
             this = this_obj;            
        end      
        
        function dataSize = getDataSize(this, ModType)
            selectModType(this, ModType);
            dataSize = this.selectedModType.mod.M;
        end
      
        function Symbol = GetSymbol(this, data, ModType)          
            selectModType(this, ModType); 
%             Symbol = this.selectedModType.mod(data, this.selectedModType.M); 
            Symbol = modulate(this.selectedModType.mod, data);
        end
        
        function [binData, decData] = GetData(this, Symbol, ModType)
            selectModType(this, ModType);
%             decData = reshape(this.selectedModType.demod(Symbol, this.selectedModType.M), 1, []);          
%             binData = reshape(dec2bin( decData', log2(this.selectedModType.M) )', 1, []) - '0';
            Symbol = reshape(Symbol, size(Symbol, 1), []);
            decData = reshape(demodulate(this.selectedModType.demod, Symbol), 1, []);          
            binData = reshape(dec2bin( decData', log2(this.selectedModType.mod.M) )', 1, []) - '0';            
        end
    end
    
end

function initialise(this)    
    this.ModType.BPSK.mod = modem.pskmod('M', 2,'InputType','integer','SYMBOLORDER','gray');
    this.ModType.BPSK.demod = modem.pskdemod('M', 2,'OutputType','integer','SYMBOLORDER','gray','DecisionType','hard decision');

    this.ModType.QPSK.mod = modem.qammod('M', 4,'InputType','integer','SYMBOLORDER','gray');
    this.ModType.QPSK.demod = modem.qamdemod('M', 4,'OutputType','integer','SYMBOLORDER','gray','DecisionType','hard decision');

    this.ModType.PSK8.mod = modem.qammod('M', 8,'InputType','integer','SYMBOLORDER','gray');
    this.ModType.PSK8.demod = modem.qamdemod('M', 8,'OutputType','integer','SYMBOLORDER','gray','DecisionType','hard decision');

    this.ModType.QAM16.mod = modem.qammod('M', 16,'InputType','integer','SYMBOLORDER','gray');
    this.ModType.QAM16.demod = modem.qamdemod('M', 16,'OutputType','integer','SYMBOLORDER','gray','DecisionType','hard decision');

    this.ModType.QAM32.mod = modem.qammod('M', 32,'InputType','integer','SYMBOLORDER','gray');
    this.ModType.QAM32.demod = modem.qamdemod('M', 32,'OutputType','integer','SYMBOLORDER','gray','DecisionType','hard decision');

    this.ModType.QAM64.mod = modem.qammod('M', 64,'InputType','integer','SYMBOLORDER','gray'); 
    this.ModType.QAM64.demod = modem.qamdemod('M', 64,'OutputType','integer','SYMBOLORDER','gray','DecisionType','hard decision');
end

function selectModType(this, ModType)   
    switch (ModType)
        case 'BPSK'
            this.selectedModType = this.ModType.BPSK;
            
        case 'QPSK'
            this.selectedModType = this.ModType.QPSK;                           

        case 'PSK8'   
            this.selectedModType = this.ModType.PSK8;                                                

        case 'QAM16'   
            this.selectedModType = this.ModType.QAM16;                              

        case 'QAM32'   
            this.selectedModType = this.ModType.QAM32;
            
        case 'QAM64'                  
            this.selectedModType = this.ModType.QAM64;

        otherwise 
            %report error
    end  
end
